package com.sneaker.shower.service.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Lists;
import com.sneaker.shower.common.BizException;
import com.sneaker.shower.common.ThreadLocalCache;
import com.sneaker.shower.entity.domain.BaseDomain;
import com.sneaker.shower.entity.domain.message.MessageDO;
import com.sneaker.shower.entity.domain.message.SessionDO;
import com.sneaker.shower.entity.domain.organization.OrganizationDO;
import com.sneaker.shower.entity.domain.sys.SysUserDO;
import com.sneaker.shower.entity.domain.user.UserDO;
import com.sneaker.shower.entity.domain.user.UserRemarkDO;
import com.sneaker.shower.entity.dto.*;
import com.sneaker.shower.mapper.SessionMapper;
import com.sneaker.shower.mapstruct.MessageMapStruct;
import com.sneaker.shower.mapstruct.UserMapStruct;
import com.sneaker.shower.service.*;
import com.sneaker.shower.socket.Channel;
import com.sneaker.shower.socket.Group;
import com.sneaker.shower.socket.common.PSWsRequest;
import com.sneaker.shower.socket.feature.SingelSign;
import com.sneaker.shower.socket.repository.GroupRepository;
import com.sneaker.shower.util.JacksonUtils;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.template.WxMpTemplateData;
import me.chanjar.weixin.mp.bean.template.WxMpTemplateMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Base64Utils;
import org.springframework.util.StringUtils;

import java.io.File;
import java.io.FileInputStream;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Service
@Slf4j
public class KeFuServiceImpl extends ServiceImpl<SessionMapper, SessionDO> implements KefuService {
    private static final ExecutorService EXECUTOR_SERVICE = new ThreadPoolExecutor(4, 8, 20, TimeUnit.MINUTES, new LinkedBlockingQueue<>());
    private final String ERROR_MSG = "暂未与客服创建连接，您可前往球鞋澡堂小程序，选择门店后进入客服回话重试";
    private GroupRepository<Channel> groupRepository;
    private UserService userService;
    private SysUserService sysUserService;
    private MessageService messageService;
    private WxMaService wxMaService;
    private WxMpService wxMpService;
    private FileService fileService;
    private OrganizationService organizationService;
    @Value("${newOrderId}")
    private String NEW_ORDER_ID;
    @Value("${trackingMessageId}")
    private String TRACKING_MESSAGE_ID;
    @Value("${newMessageId}")
    private String NEW_MESSAGE__ID;
    @Value("${homePage}")
    private String HOME_PAGE;
    private ObjectMapper objectMapper = new ObjectMapper();
    private AtomicInteger index = new AtomicInteger(0);
    private UserRemarkService userRemarkService;

    @Autowired
    public KeFuServiceImpl(@Qualifier("groupRepository") GroupRepository<Channel> groupRepository, UserService userService,
                           SysUserService sysUserService, MessageService messageService,
                           FileService fileService,
                           WxMpService wxMpService,
                           OrganizationService organizationService,
                           WxMaService wxMaService, UserRemarkService userRemarkService) {
        this.groupRepository = groupRepository;
        this.userService = userService;
        this.sysUserService = sysUserService;
        this.messageService = messageService;
        this.wxMaService = wxMaService;
        this.wxMpService = wxMpService;
        this.fileService = fileService;
        this.organizationService = organizationService;
        this.userRemarkService = userRemarkService;
    }

    @Override
    public String handleMsg(WxKeFuMsgDTO keFuMsgDTO) {
        UserDTO userDTO = ThreadLocalCache.get();
        EXECUTOR_SERVICE.submit(() -> {
            try {
                ThreadLocalCache.set(userDTO);
                if ("event".equals(keFuMsgDTO.getMsgType())) {
                    return handleEvent(keFuMsgDTO);
                }
                return handleMessage(keFuMsgDTO);
            } catch (Exception e) {
                log.error("~", e);
            } finally {
                ThreadLocalCache.set(null);
            }
            return "success";
        });
        return "success";
    }

    private String handleMessage(WxKeFuMsgDTO keFuMsgDTO) {
        UserDO userDO = userService.getOne(new LambdaQueryWrapper<UserDO>().eq(UserDO::getOpenId, keFuMsgDTO.getFromUserName()));
        SessionDO sessionDO = baseMapper.selectOne(
                new LambdaQueryWrapper<SessionDO>()
                        .eq(SessionDO::getUserGuid, userDO.getGuid())
                        .orderByDesc(SessionDO::getUpdateTime)
                        .last("limit 1")
        );
        if (sessionDO == null) {
            try {
                WxMaKefuMessage wxMpKefuMessage;
                wxMpKefuMessage = WxMaKefuMessage.newTextBuilder().toUser(userDO.getOpenId()).content(ERROR_MSG).build();
                wxMaService.getMsgService().sendKefuMsg(wxMpKefuMessage);
            } catch (WxErrorException e) {
                log.error("向用户发送消息失败：{}", e.getError());
            }
            return "success";
        }
        OrganizationDO byId = organizationService.getById(sessionDO.getOrganizationGuid());
        String[] args = {byId.getStoreName(), byId.getStoreName(), LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))};
        sendMessage(sessionDO, keFuMsgDTO.getMsgType(), keFuMsgDTO.getContent(), keFuMsgDTO.getMediaId(), args, 1);
        return "success";
    }

    @Override
    public void sendMessage(String messageType, String content, String[] args, Integer type) {
        try {
            UserDTO userDTO = ThreadLocalCache.get();
            SessionDO sessionDO = createSession(Long.parseLong(userDTO.getGuid()), Long.parseLong(userDTO.getOrganizationGuid()));
            log.info("订单信息变更发送客服消息，session:{}", JacksonUtils.writeValueAsString(sessionDO));
            sendMessage(sessionDO, messageType, content, null, args, type);
        } catch (Exception e) {
            log.error("发送客服消息发生异常");
            log.error("用户信息：【{}】\n门店信息：【{}】\n消息类型：【{}】\n消息内容：【{}】",
                    JacksonUtils.writeValueAsString(ThreadLocalCache.get()),
                    ThreadLocalCache.getOrg(),
                    messageType,
                    content
            );
            log.error("异常信息：" + e);
        }
    }

    @Override
    public void sendMessage(SessionDO sessionDO, String msgType, String content, String mediaId, String[] args, Integer type) {
        MessageDO messageDO = new MessageDO();
        messageDO.setUserGuid(sessionDO.getUserGuid());
        messageDO.setSysUserGuid(sessionDO.getSysUserGuid());
        messageDO.setMessageType(msgType);
        messageDO.setOrganizationGuid(sessionDO.getOrganizationGuid());
        messageDO.setSessionGuid(sessionDO.getGuid());
//        message.setGuid(keFuMsgDTO.getMsgId());
        messageDO.setGuid(IdWorker.getId());
        messageDO.setType(0);
        UserDO userDO = userService.getById(sessionDO.getUserGuid());
        if (messageDO.getMessageType().equals("image")) {
            try {
                File file = wxMaService.getMediaService().getMedia(mediaId);
                String path = fileService.upload(file.getName(), new FileInputStream(file));
                messageDO.setContent(path);
                messageDO.setContent(Base64Utils.encodeToString(path.getBytes()));
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            messageDO.setContent(StringUtils.hasText(content) ? Base64Utils.encodeToString(content.getBytes()) : content);
        }
        sessionDO.setUpdateTime(LocalDateTime.now());
        baseMapper.updateById(sessionDO);
        messageService.save(messageDO);
        sendMessage(sessionDO, messageDO, userDO, args, type);
    }

    /**
     * 向客服发送消息
     *
     * @param sessionDO session
     * @param messageDO message
     * @param userDO    user
     */
    private void sendMessage(SessionDO sessionDO, MessageDO messageDO, UserDO userDO, String[] args, Integer type) {
        Group<Channel> channelGroup = groupRepository.get(new SingelSign(sessionDO.getSysUserGuid() + ""));
        if (channelGroup != null && !channelGroup.group().isEmpty()) {
            PSWsRequest psWsRequest = new PSWsRequest();
            List<MessageDO> messageDOS = messageService.list(
                    new LambdaQueryWrapper<MessageDO>()
                            .eq(MessageDO::getSessionGuid, sessionDO.getGuid())
                            .le(BaseDomain::getGuid, messageDO.getGuid())
                            .orderByDesc(BaseDomain::getGuid)
                            .last("limit 10")
            );
            SessionDTO sessionDTO = new SessionDTO(
                    messageDOS.stream().map(message1 -> {
                        MessageDTO messageDTO = MessageMapStruct.INSTANCE.do2DTO(message1);
                        messageDTO.setSessionGuid(String.valueOf(message1.getSessionGuid()));
                        messageDTO.setContent(new String(Base64Utils.decodeFromString(messageDO.getContent())));
                        return messageDTO;
                    }).collect(Collectors.toList()),
                    MessageMapStruct.INSTANCE.do2DTO(messageDO),
                    String.valueOf(sessionDO.getGuid())
                    , UserMapStruct.INSTANCE.do2DTO(userDO)
            );
            try {
                psWsRequest.setContent(objectMapper.writeValueAsString(sessionDTO));
                channelGroup.group().forEach(e -> {
                    e.send(psWsRequest);
                });
            } catch (Exception e) {
                log.info("客服消息发送失败, 将通过微信公众号推送消息 \n" + e.getCause());
                sendTemplateMsg(sessionDO, args, type);
            }
        } else {//客服没在线 给客服发送
//            Organization byId = organizationService.getById(ThreadLocalCache.get().getOrganizationGuid());
//            String[] args = {byId.getName(), byId.getName() + "所属客服", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")).toString()};
            sendTemplateMsg(sessionDO, args, type);
        }
    }

    @Override
    public List<SessionDTO> history(Long sysUserGuid, Integer type) {
        List<SessionDO> sessionDO = baseMapper.selectList(
                new LambdaQueryWrapper<SessionDO>()
                        .eq(SessionDO::getSysUserGuid, sysUserGuid)
//                        .gt(Session::getUpdateTime, LocalDateTime.now().minusHours(48))
                        .orderByDesc(SessionDO::getUpdateTime)
        );
        if (sessionDO.isEmpty()) {
            log.info("当前客服【{}】暂无消息", sysUserGuid);
            return Collections.emptyList();
        }
        List<Long> userGuidS = sessionDO.stream().map(SessionDO::getUserGuid).collect(Collectors.toList());
        List<UserDO> userDOS = userService.list(new LambdaQueryWrapper<UserDO>().in(UserDO::getGuid, userGuidS));
        Map<Long, UserDO> ref = userDOS.stream().collect(Collectors.toMap(UserDO::getGuid, Function.identity()));
        return sessionDO.stream().map((e) -> {
            SessionDTO sessionDTO = new SessionDTO();
            sessionDTO.setSessionGuid(String.valueOf(e.getGuid()));
            sessionDTO.setHistoryMessage(history(e.getGuid(), Long.MAX_VALUE, type));
            UserDTO user = Optional.ofNullable(ref.get(e.getUserGuid())).map(UserMapStruct.INSTANCE::do2DTO).orElse(new UserDTO());
            decodeUserName(e, user);
            sessionDTO.setUser(user);
            return sessionDTO;
        }).collect(Collectors.toList());
    }

    @Override
    public List<MessageDTO> history(Long sessionGuid, Long startGuid, Integer type) {
        List<MessageDO> list = messageService.list(
                new LambdaQueryWrapper<MessageDO>()
                        .eq(MessageDO::getSessionGuid, sessionGuid)
        );
        Stream<MessageDTO> messageDTOStream = list.stream().map(message -> {
            MessageDTO messageDTO = MessageMapStruct.INSTANCE.do2DTO(message);
            messageDTO.setSessionGuid(String.valueOf(message.getSessionGuid()));
            messageDTO.setContent(new String(Base64Utils.decodeFromString(messageDTO.getContent())));
            return messageDTO;
        });
        if (Objects.equals(1, type)) {
            return messageDTOStream.sorted(Comparator.comparing(MessageDTO::getSendTime).reversed()).collect(Collectors.toList());
        } else {
            return messageDTOStream.collect(Collectors.toList());
        }
    }

    @Override
    public List<SessionListDTO> listSession(SessionMsgReqDTO sessionMsgReqDTO) {
        LambdaQueryWrapper<SessionDO> query = new LambdaQueryWrapper<>();
        if (StringUtils.hasText(sessionMsgReqDTO.getReqData())) {
            // 模糊查询输入的用户信息
            List<UserDO> list = userService.list(new LambdaQueryWrapper<UserDO>().select(UserDO::getGuid, UserDO::getNickName));
            List<Long> userGuidList = list.stream().filter(e ->
                    new String(Base64Utils.decodeFromString(e.getNickName())).contains(sessionMsgReqDTO.getReqData())
            ).map(UserDO::getGuid).collect(Collectors.toList());
            // 拼装查询条件
            if (CollectionUtils.isEmpty(userGuidList)) {
                return Lists.newArrayList();
            }
            query.in(SessionDO::getUserGuid, userGuidList);
        }
        UserDTO userDTO = ThreadLocalCache.get();
        query.eq(SessionDO::getOrganizationGuid, userDTO.getOrganizationGuid())
                .eq(SessionDO::getSysUserGuid, userDTO.getGuid())
                .orderByDesc(SessionDO::getUpdateTime)
                .last("limit " + sessionMsgReqDTO.getPageSize());
        List<SessionDO> list = list(query);
        if (CollectionUtils.isEmpty(list)) {
            return Lists.newArrayList();
        }
        List<Long> userGuidS = list.stream().map(SessionDO::getUserGuid).collect(Collectors.toList());
        List<UserDO> userDOS = userService.list(new LambdaQueryWrapper<UserDO>().in(UserDO::getGuid, userGuidS));
        Map<Long, UserDO> ref = userDOS.stream().collect(Collectors.toMap(UserDO::getGuid, Function.identity()));
        return list.stream().map((e) -> {
            SessionListDTO sessionListDTO = new SessionListDTO();
            sessionListDTO.setSessionGuid(String.valueOf(e.getGuid()));
            MessageDO one = messageService.getOne(new LambdaQueryWrapper<MessageDO>().eq(MessageDO::getSessionGuid, e.getGuid()).orderByDesc(MessageDO::getSendTime).last("limit 1"));
            if (one != null) {
                sessionListDTO.setContent(new String(Base64Utils.decodeFromString(one.getContent())));
                sessionListDTO.setMessageType(one.getMessageType());
                sessionListDTO.setType(one.getType());
                sessionListDTO.setSendTime(one.getSendTime());
                sessionListDTO.setHasRead(one.getHasRead());
            }
            UserDTO user = Optional.ofNullable(ref.get(e.getUserGuid())).map(UserMapStruct.INSTANCE::do2DTO).orElse(new UserDTO());
            decodeUserName(e, user);
            sessionListDTO.setUser(user);
            return sessionListDTO;
        }).collect(Collectors.toList());
    }

    @Override
    public MessageRespDTO listMsg(SessionMsgReqDTO sessionMsgReqDTO) {
        SessionDO sessionDO = this.getById(sessionMsgReqDTO.getReqData());
        if (sessionDO == null) {
            throw new BizException("会话不存在!");
        }
        UserDTO user = UserMapStruct.INSTANCE.do2DTO(userService.getById(sessionDO.getUserGuid()));
        decodeUserName(sessionDO, user);
        List<MessageDO> list = messageService.list(new LambdaQueryWrapper<MessageDO>()
                .eq(MessageDO::getSessionGuid, sessionMsgReqDTO.getReqData())
                .orderByDesc(MessageDO::getSendTime)
                .last("limit " + sessionMsgReqDTO.getPageSize()));
        if (list == null) {
            return MessageRespDTO.builder().userDTO(user).build();
        }
        List<MessageDTO> messageDTOS = list.stream().map((e) -> {
            MessageDTO messageDTO = MessageMapStruct.INSTANCE.do2DTO(e);
            messageDTO.setUserDTO(user);
            messageDTO.setContent(new String(Base64Utils.decodeFromString(e.getContent())));
            return messageDTO;
        }).collect(Collectors.toList());
        Collections.reverse(messageDTOS);
        return MessageRespDTO.builder()
                .userDTO(user)
                .messageDTOS(messageDTOS)
                .build();
    }

    private void decodeUserName(SessionDO sessionDO, UserDTO user) {
        user.setNickName(new String(Base64Utils.decodeFromString(user.getNickName())));
        UserRemarkDO userRemarkDO = userRemarkService.getOne(new LambdaQueryWrapper<UserRemarkDO>()
                .eq(UserRemarkDO::getUserGuid, Long.parseLong(user.getGuid()))
                .eq(UserRemarkDO::getOrganizationGuid, sessionDO.getOrganizationGuid()));
        if (userRemarkDO != null) {
            user.setRemarkName(new String(Base64Utils.decodeFromString(userRemarkDO.getRemarkName())));
        } else {
            user.setRemarkName(new String(Base64Utils.decodeFromString(user.getRemarkName())));
        }
    }

    /**
     * 客服向用户发送消息
     *
     * @param messageDTO messageDto
     * @return string
     */
    @Override
    @Transactional
    public String message(MessageDTO messageDTO) {
        SessionDO sessionDO = baseMapper.selectById(Long.parseLong(messageDTO.getSessionGuid()));
        if (sessionDO == null) {
            log.error("未查询到会话【{}】信息", messageDTO.getSessionGuid());
            throw new BizException("会话不存在");
        }
        MessageDO messageDO = MessageMapStruct.INSTANCE.dto2DO(messageDTO);
        messageDO.setType(1);
        UserDTO userDTO = ThreadLocalCache.get();
        messageDO.setOrganizationGuid(Long.parseLong(userDTO.getOrganizationGuid()));
        messageDO.setSysUserGuid(Long.parseLong(userDTO.getGuid()));
        messageDO.setSendTime(LocalDateTime.now());
        messageDO.setUserGuid(sessionDO.getUserGuid());
        messageDO.setGuid(IdWorker.getId());
        messageDO.setSessionGuid(Long.parseLong(messageDTO.getSessionGuid()));
        messageDO.setHasRead(Boolean.TRUE);
        messageDO.setContent(Base64Utils.encodeToString(messageDO.getContent().getBytes()));
        messageService.save(messageDO);
        UserDO userDO = userService.getOne(new LambdaQueryWrapper<UserDO>().eq(UserDO::getGuid, messageDO.getUserGuid()));
        sessionDO.setUpdateTime(LocalDateTime.now());
        baseMapper.updateById(sessionDO);
        try {
//            wxMpKefuService.kfSessionCreate(user.getOpenId(),kfAccount);
            WxMaKefuMessage wxMpKefuMessage;
            if (messageDO.getMessageType().equals("image")) {
//                WxMediaUploadResult result = wxMaService.getMediaService().uploadMedia("image", fileService.download(message.getContent()));
                WxMediaUploadResult result = wxMaService.getMediaService().uploadMedia("image", fileService.download(new String(Base64Utils.decodeFromString(messageDO.getContent()))));
                wxMpKefuMessage = WxMaKefuMessage.newImageBuilder().toUser(userDO.getOpenId()).mediaId(result.getMediaId()).build();
            } else {
//                wxMpKefuMessage = WxMaKefuMessage.newTextBuilder().toUser(user.getOpenId()).content(message.getContent()).build();
                wxMpKefuMessage = WxMaKefuMessage.newTextBuilder().toUser(userDO.getOpenId()).content(new String(Base64Utils.decodeFromString(messageDO.getContent()))).build();
            }
            wxMaService.getMsgService().sendKefuMsg(wxMpKefuMessage);

        } catch (WxErrorException e) {
            log.error("向用户发送消息失败：{}", e.getError());
            if (e.getError().getErrorCode() == 45047) {
                throw new BizException("回复消息次数已超过最大数量限制，请等待用户发送消息后回复");
            }
            if (e.getError().getErrorCode() == 45015) {
                throw new BizException("回复时间已超过48小时，你可使用下方订单通知按钮通知用户");
            }
            throw new BizException("消息发送失败");
        }
        return "success";
    }

    /**
     * 下单过后调用这个方法 发模板消息给客服
     *
     * @param args args
     * @param type type
     */
    private void sendTemplateMsg(SessionDO sessionDO, String[] args, int type) {
        WxMpTemplateMessage message;
        if (sessionDO == null) {
            throw new BizException("无法找到客服");
        }
        SysUserDO sysUserDO = sysUserService.getById(sessionDO.getSysUserGuid());
        String kfOpenId = sysUserDO.getOpenId();
        switch (type) { //首次下单 需分配一个客服，优先查找websocket建立了连接的客服
            case 0:
                message = createNewOrderMsg(args, kfOpenId);
                break;
            case 1://用户发消息时，根据订单号+用户id 查找并发送消息到指定客服
                message = createNotifyMsg(args, kfOpenId);
                break;
            case 2://用户补全订单信息，查找并发送消息到指定客服
                message = createUpdateMsg(args, kfOpenId);
                break;
            case 3:
                message = createOfflinePayMsg(args, kfOpenId);
                break;
            default:
                log.error("无法识别当前消息类别：【{}】", type);
                return;
        }

        try { // 发送消息
            wxMpService.getTemplateMsgService().sendTemplateMsg(message);
        } catch (WxErrorException e) {
            log.error("发送客服消息失败， 失败原因\n" + e);
        }
    }

    private String handleEvent(WxKeFuMsgDTO keFuMsgDTO) {
        String organizationGuid = keFuMsgDTO.getSessionFrom();

        UserDO userDO = userService.getOne(new LambdaQueryWrapper<UserDO>().eq(UserDO::getOpenId, keFuMsgDTO.getFromUserName()));
        if (userDO == null) {
            throw new BizException("用户不存在");
        }
        Long userGuid = userDO.getGuid();
        if (StringUtils.isEmpty(organizationGuid)) {
            return "success";
        }
        SessionDO sessionDO = createSession(userGuid, Long.valueOf(organizationGuid));
        if (sessionDO == null) {
            WxMaKefuMessage message = new WxMaKefuMessage();
            message.setMsgType(WxConsts.KefuMsgType.TEXT);
            message.setToUser(keFuMsgDTO.getFromUserName());
            message.setText(new WxMaKefuMessage.KfText("当前城市暂无客服人员"));
            try {
                wxMaService.getMsgService().sendKefuMsg(message);
            } catch (WxErrorException e) {
                log.info("创建session: 发送客服消息发生异常," + e);
            }
        }
        return "success";
    }

    @Override
    public SessionDO createSession(Long userGuid, Long organizationGuid) {
        SessionDO sessionDO = baseMapper.selectOne(
                new LambdaQueryWrapper<SessionDO>()
                        .eq(SessionDO::getUserGuid, userGuid)
                        .eq(SessionDO::getOrganizationGuid, organizationGuid)
                        .orderByDesc(SessionDO::getUpdateTime)
                        .last("limit 1")
        );

        if (sessionDO == null) {
            SessionDO neo = new SessionDO();
            neo.setUserGuid(userGuid);
            neo.setStartTime(LocalDateTime.now());
            neo.setOrganizationGuid(organizationGuid);
            neo.setGuid(IdWorker.getId());
            sessionDO = neo;
        }
        sessionDO.setUpdateTime(LocalDateTime.now());
        if (sessionDO.getSysUserGuid() == null) {
            List<SysUserDO> sysUserDOS = sysUserService.list(new LambdaQueryWrapper<SysUserDO>()
                    .eq(SysUserDO::getOrganizationGuid, organizationGuid)
                    .eq(SysUserDO::getRole, 1)
                    .orderByAsc(SysUserDO::getOrderCount)
            );
            if (CollectionUtils.isEmpty(sysUserDOS)) {
                OrganizationDTO mainOrg = organizationService.getMainOrg();
                log.error("当前门店【{}】暂无客服，消息将转发至总店【{}】客服", organizationGuid, mainOrg.getGuid());
                return createSession(userGuid, Long.valueOf(mainOrg.getGuid()));
            }
            List<Group<Channel>> guids = sysUserDOS.stream().map((e) -> {
                try {
                    return groupRepository.get(new SingelSign(e.getGuid() + ""));
                } catch (Exception e1) {
                    e1.printStackTrace();
                }
                return null;
            }).collect(Collectors.toList());
            if (!guids.isEmpty()) {
                boolean success = false;
                for (Group<Channel> channelGroup : guids) {
                    if (channelGroup != null && !channelGroup.group().isEmpty()) {
                        log.info("channelGroup:::{}", channelGroup);
                        success = true;
                        sessionDO.setSysUserGuid(Long.valueOf((String) channelGroup.sign()));
                        break;
                    }
                }
                if (!success) {
                    log.info("channelGroup is empty {}", sysUserDOS.get(index.get()).getGuid());
                    sessionDO.setSysUserGuid(sysUserDOS.get(0).getGuid());
                }
            } else {
                sessionDO.setSysUserGuid(sysUserDOS.get(this.index.updateAndGet((e) -> {
                    if (e >= guids.size()) {
                        return 0;
                    }
                    return e + 1;
                })).getGuid());
            }
            SysUserDO byId = sysUserService.getById(sessionDO.getSysUserGuid());
            byId.setOrderCount(byId.getOrderCount() + 1);
            sysUserService.updateById(byId);
        }
        saveOrUpdate(sessionDO);
        return sessionDO;
    }


    /**
     * 用户下单时调用该方法，向对应客服发送公众号模板消息
     *
     * @param args     {订单号， 商品名， 订单预估金额， 下单时间}
     * @param kfOpenId kfOpenId
     * @return message
     */
    private WxMpTemplateMessage createNewOrderMsg(String[] args, String kfOpenId) {

        List<WxMpTemplateData> dataList = Lists.newArrayList();
        setMessageData(args, dataList);
        WxMpTemplateData first = new WxMpTemplateData();
        first.setName("first");
        first.setValue("您有一笔新订单，请及时处理");
        dataList.add(first);

        return WxMpTemplateMessage.builder().data(dataList).templateId(NEW_ORDER_ID).toUser(kfOpenId).url(HOME_PAGE).build();
    }


    /**
     * todo 用户发送消息时调用该方法，向对应客服发送公众号模板消息
     *
     * @param args     {客服所在门店地址， 客服名字， 消息时间}//内容顺序需保持一致
     * @param kfOpenId kefuOpenId
     * @return return
     */
    private WxMpTemplateMessage createNotifyMsg(String[] args, String kfOpenId) {
        List<WxMpTemplateData> dataList = Lists.newArrayList();
        WxMpTemplateData first = new WxMpTemplateData();
        first.setName("first");
        first.setValue("有用户向你发送消息，请及时查看并回复");
        setMessageData(args, dataList);
        dataList.add(first);
        return WxMpTemplateMessage.builder().data(dataList).templateId(NEW_MESSAGE__ID).toUser(kfOpenId).url(HOME_PAGE).build();
    }

    /**
     * todo 用户发送消息时调用该方法，向对应客服发送公众号模板消息
     *
     * @param args     {客服所在门店地址， 客服名字， 消息时间}//内容顺序需保持一致
     * @param kfOpenId kfOpenId
     * @return return
     */
    private WxMpTemplateMessage createOfflinePayMsg(String[] args, String kfOpenId) {
        List<WxMpTemplateData> dataList = Lists.newArrayList();
        WxMpTemplateData first = new WxMpTemplateData();
        first.setName("first");
        first.setValue("线下订单支付成功，请前往客服中心查看收款情况");
        setMessageData(args, dataList);
        dataList.add(first);
        return WxMpTemplateMessage.builder().data(dataList).templateId(NEW_MESSAGE__ID).toUser(kfOpenId).url(HOME_PAGE).build();
    }

    /**
     * todo 用户修改订单快递时调用该方法，向对应客服发送公众号模板消息
     *
     * @param args     {用户昵称， 订单号， 发出时间， 快递公司， 快递单号}//内容顺序需保持一致
     * @param kfOpenId kfOpenId
     * @return return
     */
    private WxMpTemplateMessage createUpdateMsg(String[] args, String kfOpenId) {
        List<WxMpTemplateData> dataList = Lists.newArrayList();
        WxMpTemplateData first = new WxMpTemplateData();
        first.setName("first");
        first.setValue("用户已寄出商品，请及时与用户确认");
        dataList.add(first);
        setMessageData(args, dataList);
        return WxMpTemplateMessage.builder().data(dataList).templateId(TRACKING_MESSAGE_ID).toUser(kfOpenId).url(HOME_PAGE).build();
    }

    private void setMessageData(String[] args, List<WxMpTemplateData> dataList) {
        for (int i = 1; i <= args.length; i++) {
            WxMpTemplateData keyWord = new WxMpTemplateData();
            keyWord.setName("keyword" + i);
            keyWord.setValue(args[i - 1]);
            dataList.add(keyWord);
        }
        WxMpTemplateData remark = new WxMpTemplateData();
        remark.setName("remark");
        remark.setValue("点击进入客服系统");
        dataList.add(remark);
    }

    @Override
    public Boolean change(String sessionGuid, String sysUserGuid) {

        SessionDO sessionDO = baseMapper.selectById(Long.parseLong(sessionGuid));
        if (sessionDO == null) {
            throw new BizException("无法转移会话, 未找到会话");
        }
        SysUserDO sysUserDO = sysUserService.getOne(new LambdaQueryWrapper<SysUserDO>().eq(BaseDomain::getGuid, sysUserGuid).eq(SysUserDO::getRole, 1));
        if (sysUserDO == null) {
            throw new BizException("无法转移会话, 非法客服");
        }
        SessionDO neo = new SessionDO();
        neo.setSysUserGuid(sysUserDO.getGuid());
        neo.setGuid(sessionDO.getGuid());
        baseMapper.updateById(neo);
        return true;
    }

    @Override
    public Boolean readMessage(String sessionGuid) {
        MessageDO messageDO = new MessageDO();
        messageDO.setHasRead(true);
        return messageService.update(messageDO, new LambdaQueryWrapper<MessageDO>().eq(MessageDO::getSessionGuid, Long.parseLong(sessionGuid)));
    }

}
